package cn.okcode.api.framework.dispatcher;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ClassUtil;
import cn.okcode.api.framework.*;
import cn.okcode.api.framework.annotation.*;
import cn.okcode.api.framework.result.*;
import cn.okcode.api.framework.interceptor.InterceptorHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author pyk13
 */
public class JspApiDispatcher implements IDispatcher {
    private static final Logger logger = LogManager.getLogger(JspApiDispatcher.class.getName());

    private ApplicationContext applicationContext;
    private final String[] scanPackages;

    private final List<Callee> handlers = new ArrayList<>();
    private final Map<String, Callee> handlerMappings = new HashMap<>();

    private final IHandlerResolver handlerResolver = new DefaultHandlerResolver();

    public static final String RESULT_HANDLER_STR = "resultHandler";
    public static final String MINE_TYPE = "mineType";
    public static final String JS_CONTROLLER_SUFFIX = "_controller";

    private volatile boolean initialized = false;

    private final IHandlerResolver defaultHandlerResolver = new DefaultHandlerResolver();

    public JspApiDispatcher() {
        Set<String> packages = new HashSet<>();
        packages.addAll(Arrays.asList(Config.CONTROLLER_PACKAGE_NAME));
        packages.addAll(Arrays.asList(Config.SERVICE_PACKAGE_NAME));
        this.scanPackages = packages.toArray(new String[0]);
        init();
    }

    @Override
    public void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException {
        try {
            //获取处理方法
            Callee callee = handlerResolver.findHandler(handlerMappings, request);

            ActionContext context = new ActionContext(callee, request, response);

            //获取请求参数
            Object[] values;
            String result;
            try {
                values = handlerResolver.findParameterValues(callee, request);
            } catch (Jsp2ApiException e) {
                context.setThrowable(e);
                InterceptorHandler.defaultHandler.onException(context);
                result = callee.getResultHandler().err(callee, "", e);
                response.getWriter().write(result);
                return;
            }

            context.setParameters(values);
            //执行
            result = callee.call(context);

            //输出返回
            response.getWriter().write(result);
        } catch (ActionException e) {
            logger.error("action初始化异常", e);
            throw e;
        } catch (RawResultException e) {
            logger.error("执行出错", e);
            try {
                response.getWriter().write(e.getLocalizedMessage());
            } catch (IOException ex) {
                throw new ServletException(ex);
            }
            response.setStatus(500);
        } catch (Exception e) {
            logger.error("未知错误", e);
            throw new ServletException(e);
        }
    }

    @Override
    public void refresh() {
        applicationContext.refresh();
        initActions(applicationContext);
    }

    public void init() {
        if (initialized) {
            return;
        }
        synchronized (JspApiDispatcher.class) {
            if (!initialized) {
                String jarPath = this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile();
                if (jarPath != null && jarPath.endsWith(".jar")) {
                    applicationContext = new ApplicationContext(jarPath, scanPackages);
                } else {
                    applicationContext = new ApplicationContext(scanPackages);
                }
                initActions(applicationContext);
                initialized = true;
            }
        }
    }

    private void initActions(ApplicationContext context) {
        this.handlers.clear();
        this.handlerMappings.clear();
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object controller;
            try {
                controller = context.getBean(beanName);
            } catch (Exception e) {
                throw new ActionException("获取实例出错", e);
            }
            Class<?> clazz = controller.getClass();
            if (!clazz.isAnnotationPresent(CPController.class)) {
                continue;
            }

            //获取Method的url配置
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //没有加Action注解的直接忽略
                if (!method.isAnnotationPresent(CPAction.class)) {
                    continue;
                }
                //获取请求结果处理类型
                Object objResultHandler = AnnotationUtil.getAnnotationValue(method, CPAction.class, RESULT_HANDLER_STR);
                ResultHandler resultHandler = ResultHandlerFactory.createHandler((ResultHandlerEnum) objResultHandler);

                //响应头
                Object objRespContentType = AnnotationUtil.getAnnotationValue(method, CPAction.class, MINE_TYPE);
                MineType mineType = (MineType) objRespContentType;

                //是否开启权限屏蔽
                Boolean accessEnforced = AnnotationUtil.getAnnotationValue(method, CPAccessEnforced.class);
                if (accessEnforced == null) {
                    accessEnforced = false;
                }

                //是否开启事务
                Boolean transactionOpen = AnnotationUtil.getAnnotationValue(method, CPTransaction.class);
                if (transactionOpen == null) {
                    transactionOpen = false;
                }

                //获取请求处理器
                IHandlerResolver handlerResolver = defaultHandlerResolver;
                Class<? extends IHandlerResolver> handlerResolverType = AnnotationUtil.getAnnotationValue(method, CPHandlerResolver.class);
                if (handlerResolverType != null) {
                    handlerResolver = (IHandlerResolver) ApplicationContext.getCurrent().getBean(handlerResolverType);
                    Objects.requireNonNull(handlerResolver, String.format("找不到类型为%s的处理器解析器", handlerResolverType.getName()));
                }

                Callee callee = new Callee(controller, method, resultHandler);
                ParamDefinition[] paramDefinitions = initParameters(method);
                callee.setMineType(mineType);
                callee.setParamDefinitions(paramDefinitions);
                callee.setAccessEnforced(accessEnforced);
                callee.setTransactionOpen(transactionOpen);
                callee.setHandlerResolver(handlerResolver);

                //读取缓存配置
                Boolean isCache = AnnotationUtil.getAnnotationValue(method, CPCache.class);
                if (isCache != null && isCache) {
                    callee.setCache(true);
                }

                this.handlers.add(callee);
                this.handlerMappings.put(callee.getCallee(), callee);
            }
        }
    }

    public boolean isMultipart(HttpServletRequest request) {
        //"multipart/form-data"
        return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
    }

    private ParamDefinition[] initParameters(Method action) {
        Parameter[] parameters = action.getParameters();
        ParamDefinition[] paramDefinitions = new ParamDefinition[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            Map<String, Object> annoValues = AnnotationUtil.getAnnotationValueMap(parameters[i], CPParam.class);
            if (annoValues == null) {
                throw new ActionException("参数配置错误,需要CPParam注解");
            }
            String parameterName = default2String(annoValues.get("value"), "");
            if ("".equals(parameterName)) {
                throw new ActionException("参数名配置错误，注解参数名不能为空");
            }

            boolean isRequired = default2Bool(annoValues.get("required"), false);
            String blankText = "参数[" + parameterName + "]不能为空";
            if (!isRequired) {
                Map<String, Object> requiredValues = AnnotationUtil.getAnnotationValueMap(parameters[i], CPRequired.class);
                if (requiredValues != null) {
                    isRequired = default2Bool(requiredValues.get("value"), false);
                    blankText = default2String(requiredValues.get("blankText"), blankText);
                }
            }
            Parameter parameter = parameters[i];

            Class<?> parameterType = parameter.getType();
            Type parameterizedType = parameter.getParameterizedType();
            boolean isSimpleType = ClassUtil.isSimpleValueType(parameterType);

            //构造参数定义
            ParamDefinition paramDefinition = new ParamDefinition();
            paramDefinition.setName(parameterName);
            paramDefinition.setRequired(isRequired);
            if (isRequired) {
                paramDefinition.setBlankText(blankText);
            }
            paramDefinition.setParameterType(parameterType);
            paramDefinition.setParameterizedType(parameterizedType);
            paramDefinition.setSimpleType(isSimpleType);

            paramDefinitions[i] = paramDefinition;
        }
        return paramDefinitions;
    }
}
